using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Core;
using Core.Domain.Entities;
using Core.Domain.Enums;
using Core.Interfaces;
using Core.Settings;
using DataMain;

namespace TallyJ.Announcer
{
  ///<summary>Presenter for the Announcer</summary>
  public class AnnouncerPresenter
  {
    //private Timer _clockTimer;
    private int _currentIndex;

    private IList<Person> _people;
    private int _personCount;
    private IAnnouncerView _view;
    private readonly IEnvironment _environment;
    private bool _done;

    ///<summary>Default. Creates new window.</summary>
    public AnnouncerPresenter() : this(new AnnouncerWindow(), IoC.Container.Resolve<IEnvironment>())
    {
    }

    ///<summary>Can inject another view</summary>
    ///<param name="view"></param>
    ///<param name="environment"></param>
    public AnnouncerPresenter(IAnnouncerView view, IEnvironment environment)
    {
      AnnoucerDataContext = new AnnouncerDataContext();

      _view = view;
      _environment = environment;
      _view.LocalDataContext = AnnoucerDataContext;
      _view.Presenter = this;
      //_view.ViewDispatcherAction(Load);
      LoadElectionInfo();

      _environmentDataHelper = IoC.Container.Resolve<IEnvironmentDataHelper>();
      _environmentDataHelper.DataChanged+=OnDataChangedExternally;

    }
    IEnvironmentDataHelper _environmentDataHelper;

    private void OnDataChangedExternally(object sender, DataChangedEvent args)
    {
      if(args.FileSystemEventArgs.ChangeType==WatcherChangeTypes.Changed)
      {
        _environmentDataHelper.RecreateSession();
        _environmentDataHelper.AttachToCurrentData();

        var election = _environment.Election;

        var newPeople = (from v in election.Voters
                         where v.BallotSubmissionMethod == BallotSubmissionMethod.InAttendanceVoteNotYetReceived
                               && v.PersonVoting.IsAdultInGoodStanding()
                         orderby v.PersonVoting.SortableName()
                         select v.PersonVoting).ToList();

        // compare the two... add new to the end
        var comparer = new Person.CompareByVoteeId();
        var added = false;

        foreach (var person in newPeople)
        {
          if(!_people.Contains(person, comparer))
          {
            added = true;
            _people.Add(person);
          }
        }
        if(added)
        {
          _personCount = _people.Count;
          SetCurrent(_currentIndex);
        }
      }
    }

    private AnnouncerDataContext AnnoucerDataContext { get; set; }

    public void OnKeyDown(object sender, KeyEventArgs args)
    {
      switch (args.Key)
      {
        case Key.Up:
          SetCurrent(_currentIndex - 1);
          break;
        case Key.Down:
        case Key.Space:
          SetCurrent(_currentIndex + 1);
          break;
        case Key.Escape:
          _view.Close();
          break;
      }
    }

    private void SetCurrent(int i)
    {
      SetCurrent(i, true);
    }

    private void SetCurrent(int i, bool doAnimation)
    {
      if(_done)
      {
        return;
      }
      if (i < -3)
      {
        i = -3; // allow to overshoot
      }
      else if (i > _personCount)
      {
        i = _personCount;
        DoAnimation(null, new AnimateTypeArg("AnimateDone"));
        _done = true;
      }
      else
      {
        if (_currentIndex != i)
        {
          DoAnimation(null, new AnimateTypeArg(_currentIndex < i ? "AnimateUp" : "AnimateDown"));
        }
      }

      _currentIndex = i;

      AnnoucerDataContext.PastPerson1 = GetPerson(i - 1);
      AnnoucerDataContext.Current = GetPerson(i);
      AnnoucerDataContext.UpcomingPerson1 = GetPerson(i + 1);
      AnnoucerDataContext.UpcomingPerson2 = GetPerson(i + 2);
      AnnoucerDataContext.UpcomingPerson3 = GetPerson(i + 3);
    }

    private Person GetPerson(int i)
    {
      if (_people == null)
      {
        return null;
      }

      if (i < 0 || i > _personCount - 1)
      {
        return null;
      }
      return _people[i];
    }

    private void LoadElectionInfo()
    {
      // get current election
      var election = _environment.Election;

      _people = (from v in election.Voters
                 where v.BallotSubmissionMethod == BallotSubmissionMethod.InAttendanceVoteNotYetReceived
                       && v.PersonVoting.IsAdultInGoodStanding()
                 orderby v.PersonVoting.SortableName()
                 select v.PersonVoting).ToList();

      _personCount = _people.Count;

      SetCurrent(-1, false);
    }

    ///<summary>Start this view</summary>
    public void Show()
    {
      _view.Show();
    }

    ///<summary>Start this view as a dialog</summary>
    public void ShowDialog()
    {
      _view.ShowDialog();
    }

    public void OnMouseWheel(object sender, MouseWheelEventArgs e)
    {
      SetCurrent(_currentIndex + (e.Delta < 0
                                        ? 1
                                        : (e.Delta > 0
                                                 ? -1
                                                 : 0)));
    }

    public event EventHandler<AnimateTypeArg> DoAnimation;
  }
}